package com.rw.coco.expression.parser.element;


import com.rw.coco.expression.parser.element.enumeration.Sort;
import com.rw.coco.handler.message.reply.handler.command.exception.global.NumberOverflowException;
import com.rw.tool.util.expression.element.NumberElement;
import com.rw.tool.util.random.RandomUtil;
import com.rw.tool.util.string.StringFormatter;
import com.rw.tool.util.string.StringUtil;
import lombok.Getter;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 特化数字表达式
 */
@Getter
public class RdNumberElement extends NumberElement implements DiceNumberElement {
    public static final String REGEX = "(\\d*d\\d*)(k\\d+|q\\d+)?";

    public static final Pattern DICE_PATTERN = Pattern.compile("^" + REGEX + "$", Pattern.CASE_INSENSITIVE);

    // 展开展示转缩写的限额，骰子数超过这个数则转缩写
    private static final int ABBR_FACTOR = 100;

    // 排序展示骰子的限额
    private static final int SORT_FACTOR = 30;

    private final int diceCount;

    private final int diceSize;

    private final int limit;

    private final Sort sort;

    private final List<Integer> result;

    private final int sum;

    public RdNumberElement(int diceCount, int diceSize, int limit, Sort sort) {
        super(new BigDecimal(0));

        if (limit > diceCount) {
            String errMsg = "limit can not bigger than diceCount! limit:{}, diceCount:{}";
            throw new IllegalArgumentException(StringFormatter.format(errMsg, limit, diceCount));
        }

        this.diceCount = diceCount;
        this.diceSize = diceSize;
        this.limit = limit;
        this.sort = sort;

        // 计算结果
        result = new ArrayList<>(diceCount);
        for (int i = 0; i < diceCount; i++) {
            int randomNum = RandomUtil.nextInt(diceSize) + 1;
            result.add(randomNum);
        }

        this.sum = sort.sum(result, getSimulateLimit());
    }

    public RdNumberElement(int diceCount, int diceSize) {
        this(diceCount, diceSize, -1, Sort.NONE);
    }

    @Override
    public BigDecimal getNumber() {
        return new BigDecimal(sum);
    }

    @Override
    public String toString() {
        return String.valueOf(sum);
    }

    /**
     * 获取limit
     * 如果没有排序，则返回diceCount
     */
    public int getSimulateLimit() {
        if (Sort.NONE == sort) {
            return diceCount;
        }
        return limit;
    }

    /**
     * 转为骰子表达式，例如 '1d3'
     */
    @Override
    public String toDiceString() {
        if (Sort.NONE == sort) {
            return diceCount + "D" + diceSize;
        }

        if (Sort.ASC == sort) {
            return diceCount + "D" + diceSize + "Q" + limit;
        } else {
            return diceCount + "D" + diceSize + "K" + limit;
        }
    }

    /**
     * 转化为计算结果表达式，例如 '1+3'
     */
    @Override
    public String toAfterDiceString(boolean simple) {
        // 如果骰子数超过了骰子数限额，则直接显示结果
        if (diceCount > ABBR_FACTOR || simple) {
            return String.valueOf(sum);
        }

        // 如果骰子超过了排序限额，则排序显示
        if (diceCount > SORT_FACTOR && Sort.NONE == sort) {
            return result.stream()
                    .sorted()
                    .limit(getSimulateLimit())
                    .map(Object::toString)
                    .collect(Collectors.joining(" + "));
        } else {
            return result.stream()
                    .sorted(sort.comparator())
                    .limit(getSimulateLimit())
                    .map(Object::toString)
                    .collect(Collectors.joining(" + "));
        }
    }

    /**
     * 转化为计算结果表达式并加入括号，例如 '(1+3)'
     * 如果数字只有一个，则不加括号
     */
    @Override
    public String afterDiceWithCast(boolean simple) {
        if (getSimulateLimit() == 1 || simple) {
            return toAfterDiceString(simple);
        } else {
            return "(" + toAfterDiceString(false) + ")";
        }
    }

    public static RdNumberElement parse(String parseStr, int defaultDice) {
        // 匹配正则表达式
        Matcher matcher = DICE_PATTERN.matcher(parseStr);
        if (!matcher.find()) {
            return null;
        }

        // 获取diceCount和diceSize
        String[] split = matcher.group(1).split("[dD]");
        int diceCount = parseInt(split, 0, 1);
        int diceSize = parseInt(split, 1, defaultDice);

        if (diceCount <= 0 || diceSize <= 0) {
            return null;
        }

        // 如果没有取最大值和最小值则直接返回
        String limitStr = matcher.group(2);
        if (StringUtil.isBlank(limitStr)) {
            return new RdNumberElement(diceCount, diceSize);
        }

        // 获取最大值和最小值取法
        String sortStr = limitStr.substring(0, 1);
        String limitSizeStr = limitStr.substring(1);
        Sort sort = Sort.of(sortStr);
        int limit;
        try {
            limit = Integer.parseInt(limitSizeStr);
        } catch (NumberFormatException exception) {
            throw new NumberOverflowException();
        }

        if (limit <= 0) {
            return null;
        }

        return new RdNumberElement(diceCount, diceSize, limit, sort);
    }

    private static int parseInt(String[] arr, int index, int defaultNum) {
        if (arr.length <= index) {
            return defaultNum;
        }
        try {
            return Integer.parseInt(arr[index]);
        } catch (NumberFormatException e) {
            return defaultNum;
        }
    }
}
